<template>
    <div class="drawer-layout">
        <div class="drawer-wrap" :class="animateStyle" :style="drawerStyle">
            <slot name="drawer"/>
        </div>
        <div class="content-wrap" :class="contentDrawable?animateStyle:{}"
             :style="contentDrawable?contentStyle:{}">
            <div class="drawer-mask" @click="handleMaskClick" :style="{'opacity':backdropOpacity}"
                 v-show="backdrop && pos"></div>
            <slot name="content"/>
        </div>
    </div>
</template>
<script>
  const supportsPassive = (() => {
    let supportsPassive = false;
    try {
      const opts = Object.defineProperty({}, 'passive', {
        get: function () {
          supportsPassive = true;
        }
      });
      window.addEventListener("test", null, opts);
    } catch (e) {
    }
    return supportsPassive;
  })();
  const duration = 500;
  const isTouch = 'ontouchstart' in window;
  const mouseEvents = isTouch ?
  {
    down: 'touchstart',
    move: 'touchmove',
    up: 'touchend'
  } :
  {
    down: 'mousedown',
    move: 'mousemove',
    up: 'mouseup'
  };
  let containerWidth;
  export default {
    name: 'vue-drawer-layout',
    props: {
      leftWidth: {
        type: Number,
        default:500,
      },
      drawableDistance: {
        type: Number
      },
      zIndex: {
        type: Number,
        default: 818
      },
      contentDrawable: {
        type: Boolean,
        default: false
      },
      backdrop: {
        type: Boolean,
        default: true
      },
      backdropOpacityRange: {
        type: Array,
        default: function () {
          return [0, 0.4]
        },
        validator: function (value) {
          let [min, max] = value;
          return min < max && min >= 0 && max <= 1
        }
      },
      enable: {
        type: Boolean,
        default: true
      },
      animatable: {
        type: Boolean,
        default: true
      },
      reverse: {
        type: Boolean,
        default: false
      }
    },
    data() {
      return {
        width: this.leftWidth,
        distance: this.drawableDistance,
        pos: 0,
        visible: false,
        moving: false,
        willChange: false
      }
    },
    methods: {
      toggle(visible) {
        if (visible === undefined) visible = !this.visible;
        this.visible = visible;
        this.pos = visible ? this.width : 0;
        this.moving = true
      },
      handleMaskClick() {
        if (this.moving) return;
        this.$emit('mask-click')
      }
    },
    watch: {
      'moving'() {
        if (!this.animatable) this.moving = false;
      },
      'willChange'() {
        if (!this.animatable) this.willChange = false;
      }
    },
    computed: {
      animateStyle() {
        const {moving, willChange} = this;
        return {'moving': moving, 'will-change': willChange};
      },
      drawerStyle() {
        const {zIndex, width, moveRate, pos, reverse} = this;
        return {
          zIndex: zIndex,
          width: `${width}px`,
          [reverse ? 'right' : 'left']: `-${Math.ceil(width * moveRate)}px`,
          transform: `translate3d(${reverse ? '-' : ''}${Math.ceil(pos * moveRate)}px,0,0)`
        };
      },
      contentStyle() {
        const {pos, reverse} = this;
        return {transform: `translate3d(${reverse ? '-' : ''}${pos}px,0,0)`};
      },
      backdropOpacity() {
        const {backdropOpacityRange, pos, width} = this,
        [min, max] = backdropOpacityRange;
        return min + max * (pos / width) || 0;
      },
      moveRate() {
        const {width, distance} = this;
        return distance / width;
      }
    },
    mounted() {
      const container = this.$el, containerWidth = parseInt(window.getComputedStyle(this.$el.parentNode).width);
      let defaultWidth = containerWidth * 0.8;
      this.width = this.width || defaultWidth;
      this.distance = this.distance || defaultWidth;
      const {width, reverse} = this;
      let t1, t2, speed, startX, startY, nowX, nowY, lastX, startPos, isVerticle;
      const initDrag = function (e) {
        if (!this.enable) return;
        this.willChange = true;
        isVerticle = undefined;
        nowX = startX = e.clientX || e.changedTouches[0].clientX;
        startY = e.clientY || e.changedTouches[0].clientY;
        t2 = +new Date();
        startPos = this.pos;
        document.addEventListener(mouseEvents.move, drag, isTouch && supportsPassive ? {passive: true} : false);
        document.addEventListener(mouseEvents.up, removeDrag, isTouch && supportsPassive ? {passive: true} : false);
        this.$emit('slide-start')
      }.bind(this);
      const drag = function (e) {
        t1 = t2;
        t2 = +new Date();
        lastX = nowX;
        nowX = e.clientX || e.changedTouches[0].clientX;
        nowY = e.clientY || e.changedTouches[0].clientY;
        speed = [1, -1][+reverse] * (nowX - lastX) / (t2 - t1);
        let pos = startPos + [1, -1][+reverse] * (nowX - startX);
        pos = Math.min(width, pos);
        pos = Math.max(0, pos);
        if (isVerticle === undefined) isVerticle = Math.abs(nowX - startX) / Math.abs(nowY - startY) < Math.sqrt(3);
        if (!isVerticle) {
          if (!(isTouch && supportsPassive)) e.preventDefault();
          this.pos = pos;
          this.$emit('slide-move', pos);
        }
      }.bind(this);
      const removeDrag = function (e) {
        if (isVerticle !== undefined) {
          if (!isVerticle) {
            let pos = this.pos;
            if (speed > 0) {
              this.visible = (width - pos) / speed < duration || pos > width * 3 / 5
            } else {
              this.visible = !((0 - pos) / speed < duration || pos < width * 3 / 5)
            }
            if (this.pos > 0 && this.pos < width) this.moving = true;
          }
          this.pos = this.visible ? width : 0;
        }
        if (!this.moving) {
          this.willChange = false;
          this.$emit('slide-end', this.visible);
        }
        isVerticle = undefined;
        document.removeEventListener(mouseEvents.move, drag, isTouch && supportsPassive ? {passive: true} : false);
        document.removeEventListener(mouseEvents.up, removeDrag, isTouch && supportsPassive ? {passive: true} : false);
      }.bind(this);
      'transitionend webkitTransitionEnd msTransitionEnd otransitionend oTransitionEnd'.split(' ').forEach((e) => {
        this.$el.addEventListener(e, () => {
          if (this.moving) {
            this.moving = false;
            this.willChange = false;
            this.pos = this.visible ? width : 0;
            this.$emit('slide-end', this.visible);
          }
        }, false)
      });
      container.addEventListener(mouseEvents.down, initDrag, isTouch && supportsPassive ? {passive: true} : false);
    }
  }
</script>

<style   scoped>
    .drawer-layout{
        flex: 1;
        background-color: #0088fb;
        position: relative;
    }


    .drawer-wrap{
        position: absolute;
        top: 0;
        bottom: 0;
        background-color: #666666;
        transform: translateZ(0);
        display:flex;
        flex:1;
    }

    .content-wrap{
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        overflow: hidden;
        transform: translateZ(0);
    }


    .drawer-mask{
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        background-color: #000;
        z-index: 818
    }


    .moving{
        transition: transform .3s ease
    }


    .will-change {
        will-change: transform
    }

</style>